using System;
using System.Reflection;
using System.Collections.Generic;
using System.Text;

namespace Dida
{
	internal class MethodFootprint : IComparable<MethodFootprint>,
		IEquatable<MethodFootprint>
	{
		public string name;
		public ParameterInfo[] parameters;

		public MethodFootprint(MethodInfo methodInfo)
		{
			name = methodInfo.Name;
			parameters = methodInfo.GetParameters();

		}

		#region IComparable<MethodFootprint> Members

		public int CompareTo(MethodFootprint other)
		{
			int res = name.CompareTo(other.name);
			if (res != 0)
				return res;

			int paramIndex = 0;
			while (paramIndex < parameters.Length &&
				paramIndex < other.parameters.Length)
			{
				res = CompareParams(parameters[paramIndex],other.parameters[paramIndex]);
				paramIndex++;

				if (res != 0)
					return res;				
			}

			if (parameters.Length == paramIndex)
			{
				if (other.parameters.Length == paramIndex)
					return 0;
				else
					return -1;
			}
			else
				return 1;
		}

		private static int CompareParams(ParameterInfo a, ParameterInfo b)
		{
			int res;

			res = a.ParameterType.FullName.CompareTo(b.ParameterType.FullName);
			if (res != 0)
				return res;

			if (a.IsIn && !b.IsIn)
				return -1;
			else if (!a.IsIn && b.IsIn)
				return 1;

			if (a.IsOut && !b.IsOut)
				return -1;
			else if (!a.IsOut && b.IsOut)
				return 1;

			if (a.IsRetval && !b.IsRetval)
				return -1;
			else if (!a.IsRetval && b.IsRetval)
				return 1;

			return 0;
		}

		#endregion

		public override int GetHashCode()
		{
			int ret = name.GetHashCode();
			foreach (ParameterInfo param in parameters)
				ret = (ret << 5) - 5 + param.ParameterType.GetHashCode();
			return ret;
		}

		public static bool operator ==(MethodFootprint a, MethodFootprint b)
		{
			return a.Equals(b);
		}

		public static bool operator !=(MethodFootprint a, MethodFootprint b)
		{
			return !a.Equals(b);
		}

		public override bool Equals(object obj)
		{
			return Equals((MethodFootprint)obj);
		}

		#region IEquatable<MethodFootprint> Members

		public bool Equals(MethodFootprint other)
		{
			if (name != other.name)
				return false;

			if (parameters.Length != other.parameters.Length)
				return false;

			for(int i = 0;i < parameters.Length;i++)
			{
				if (!CompareParamsForEquality(parameters[i], other.parameters[i]))
					return false;
			}

			return true;
		}

		private static bool CompareParamsForEquality(ParameterInfo a, ParameterInfo b)
		{
			if (a.ParameterType.FullName != b.ParameterType.FullName)
				return false;

			if (a.IsIn != b.IsIn)
				return false;

			if (a.IsOut != b.IsOut)
				return false;

			if (a.IsRetval != b.IsRetval)
				return false;

			return true;
		}

		#endregion
	}

	public partial class TraitClassBuilder
	{
		public class ErrorList
		{
			private List<Error> errors = new List<Error>();

			public void Add(Error err)
			{
				errors.Add(err);
			}

			public string GetConcattedMessages(int indentLevel)
			{
				StringBuilder ret = new StringBuilder();
				foreach (Error err in errors)
				{
					string tabs = new string('\t', indentLevel);
					ret.AppendLine(tabs + err.Message);
				}
				return ret.ToString();
			}

			/// <summary>
			/// Returns an array of all errors that are of the given type,
			/// or of a derived type.
			/// </summary>
			/// <param name="type">The type of the errors you want.</param>
			/// <returns>An array with the errors that are of the given type.</returns>
			public Error[] GetErrorsByType(Type type)
			{
				List<Error> ret = new List<Error>();
				foreach (Error error in errors)
					if (type.IsInstanceOfType(error))
						ret.Add(error);
				return ret.ToArray();
			}
		}

		public abstract class Error
		{
			/// <summary>
			/// Should get a human readable message, that describes this
			/// error message.
			/// </summary>
			public abstract string Message
			{
				get;
			}
		}

		public class UnresolvedImplementationError : Error
		{
			private MethodInfo method;
			private string msg;

			internal UnresolvedImplementationError(MethodInfo method)
			{
				this.method = method;

				msg = "Unresolved implementation: \"" + method.Name +
					"\" in trait \"" + method.DeclaringType.Name + "\"";
			}

			/// <summary>
			/// Gets a human readable message, that describes this error.
			/// </summary>
			public override string Message
			{
				get { return msg; }
			}
		}

		public class MultipleImplementationsError : Error
		{
			private MethodInfo method;
			private string msg;

			internal MultipleImplementationsError(MethodInfo method)
			{
				this.method = method;

				msg = "The method declaration " + method.Name + " in " +
					method.ReflectedType.FullName + " expects only one " +
					"implementation, but multiple are provided.";
			}

			public override string Message
			{
				get { return msg; }
			}
		}

		public class NoImplementationError : Error
		{
			private MethodInfo method;
			private string msg;

			internal NoImplementationError(MethodInfo method)
			{
				this.method = method;

				msg = "The trait method " + method.Name +
					" (declared in " + method.DeclaringType.FullName + ") " + 
					" requires an implementation, but none is provided";
			}

			public override string Message
			{
				get { return msg; }
			}
		}

		public class ConstructorParamsError : Error
		{
			private Type type;
			private string msg;

			internal ConstructorParamsError(Type type)
			{
				this.type = type;

				msg = "The trait " + type.FullName + " doesn't have a constructor " +
					" that doesn't take any parameters.";
			}

			/// <summary>
			/// Gets a human readable message, that describes this error.
			/// </summary>
			public override string Message
			{
				get { return msg; }
			}
		}

		public class TraitDuplicatedError : Error
		{
			private Type traitType;

			internal TraitDuplicatedError(Type traitType)
			{
				this.traitType = traitType;
			}
			/// <summary>
			/// Gets a human readable message, that describes this error.
			/// </summary>
			public override string Message
			{
				get 
				{
					return "The trait " + traitType.FullName + " is specified more than once";
				}
			}
		}

		public class TraitsInheritError : Error
		{
			private Type highestType;
			private Type[] allTypes;

			internal TraitsInheritError(Type highestType,Type[] allTypes)
			{
				this.highestType = highestType;
				this.allTypes = allTypes;
			}

			/// <summary>
			/// Gets a human readable message, that describes this error.
			/// </summary>
			public override string Message
			{
				get
				{
					string ret;
					ret = "A trait class should not contain a trait that's " +
						"derived from another trait, that's also included in " +
						"the trait class.\n";
					ret += "The following traits all directly or indirectly inherit from " + highestType.FullName + ":\n";
					foreach (Type type in allTypes)
					{
						ret += "\t" + type.FullName + "\n";
					}
					return ret;
				}
			}

			/// <summary>
			/// Gets the type of the trait that's the highest in the inheritence
			/// hierarchy
			/// </summary>
			public Type HighestType
			{
				get { return highestType; }
			}

			/// <summary>
			/// Gets an array of the types of all traits that collide at least 
			/// with the <see cref = "HighestType">highest trait</see>, and
			/// possibly with other traits aswell.
			/// </summary>
			public Type[] AllTypes
			{
				get { return allTypes; }
			}
		}
	}
}